//
//  Objcer.swift
//  AGF
//
//  Created by CS001 on 2021/3/17.
//

import Foundation

public enum Objcer {
    public enum ASPolicy: UInt {
        case assign = 0
        case retain = 1
        case copy = 3
        case atomicRetain = 769
        case atomicCopy = 771
    }
    public static func getASValue(for object: Any, key: UnsafeRawPointer) -> Any?{
        return objc_getAssociatedObject(object, key)
    }
    public static func setASValue(_ value: Any?, for object: Any, key: UnsafeRawPointer, policy: ASPolicy) {
        return objc_setAssociatedObject(object, key, value, objc_AssociationPolicy(rawValue: policy.rawValue)!)
    }
    public static func removeASValue(for object: Any, key: UnsafeRawPointer, policy: ASPolicy) {
        setASValue(nil, for: object, key: key, policy: policy)
    }
}

extension Objcer {
    public static func existingSelector(_ sel: Selector, inClass cls: AnyClass) -> Bool {
        var count: UInt32 = 0
        let methods = class_copyMethodList(cls, &count)
        var isExists = false
        for i in 0..<count {
            guard let m = methods?[Int(i)] else {
                free(methods)
                return false
            }
            let name = NSStringFromSelector(method_getName(m))
            if NSStringFromSelector(sel) == name {
                isExists = true
            }
        }
        free(methods)
        return isExists
    }
    
    public static func swizzleSelector1(_ sel1: Selector, inClass1 cls1: AnyClass, withSelector2 sel2: Selector, inClass2 cls2: AnyClass) {
        guard let m1 = class_getInstanceMethod(cls1, sel1),
              let m2 = class_getInstanceMethod(cls2, sel2) else {
            return
        }
        let isM1Added = class_addMethod(cls1, sel1, method_getImplementation(m2), method_getTypeEncoding(m2))
        if isM1Added {
            class_replaceMethod(cls2, sel2, method_getImplementation(m1), method_getTypeEncoding(m1))
        }
        else {
            method_exchangeImplementations(m1, m2)
        }
    }
}
